import math

from gmpy2 import is_zero

from playLA._globa import EPSILON


class Vector:
    def __init__(self, lst):
        # copy
        self._values = list(lst)

    def __add__(self, other):
        assert len(self) == len(other), "error, len must be same"
        return Vector([a + b for a, b in zip(self, other)])

    def __sub__(self, other):
        assert len(self) == len(other), "error, len must be same"
        return Vector([a - b for a, b in zip(self, other)])

    def __mul__(self, scalar):
        # self * scalar
        return Vector([a * scalar for a in self])

    def dot(self, other):
        # 向量点乘，返回标量
        assert len(self) == len(other), "error, len must be same"
        return sum(a * b for a, b in zip(self, other))

    def underlying_list(self):
        # 返回封装内部底层的数据【副本】
        return self._values[:]

    def __rmul__(self, scalar):
        # scalar * k
        return Vector([a * scalar for a in self])

    def __truediv__(self, scalar):
        # python3 会区分普通除法和整数除法  /  //
        return (1 / scalar) * self

    def __pos__(self):
        return 1 * self

    def __neg__(self):
        return -1 * self

    def __getitem__(self, item):
        return self._values[item]

    def __iter__(self):
        return self._values.__iter__()

    def __len__(self):
        return len(self._values)

    def __repr__(self):
        # 命令行直接输出
        return "Vector({})".format(self._values)

    def __str__(self):
        # print 调用
        return "({})".format(", ".join(str(e) for e in self._values))

    @classmethod
    def zero(cls, dim):
        return cls([0] * dim)

    def norm(self):
        # 返回一个向量的模
        return math.sqrt(sum(e ** 2 for e in self))

    def normalize(self):
        # 返回向量的单位向量
        # return Vector([ e / self.norm() for e in self]) // bad: 会多次计算 norm()
        # return 1 / self.norm() * Vector(self._values)
        # 计算机存储浮点数是近似值，浮点数等于零的判断不准确，浮点数直接比较也不准确
        if is_zero(self.norm()):
            raise ValueError("norm() cannot be zero")
        return Vector(self._values) / self.norm()  # 标量除法
